# module geometry.twoD.polyline

import copy
import math

import geometry.xy
XY = geometry.xy.XY

import base
import point
import circle
import line
import rectangle

class Polyline(base.Geometry2d):
	# Implements a poly-line along a path of points
	def __init__(self,vertices,width=0.0):
		# Constructor; takes a set of points containing vertices, and an optional width
		self.vertices = copy.deepcopy(vertices)
		self.width = width
		self._bbox = self._computeBbox()
		self._boundingCircle = self._computeBoundingCircle()
	def __repr__(self):
		# Returns the python-representation for the object
		return 'Polyline(' + str(self.vertices) + ',' + str(self.width) + ')'
	def __str__(self):
		# Returns a printable string representation of the object
		return self.__repr__()
	def isInWidth(self,pt):
		for e in self.edges():
			if e.isInwidth(pt):
				return 1
		return 0
	def isOnBoundary(self,pt,tol=0.0):
		assert isinstance(pt,point.Point),"Must provide a Point object"
		for e in self.edges():
			if e.isOnBoundary(pt,tol):
				return 1
		return 0
	def map(self,pt):
		rv = []
		for e in self.edges():
			rv.append(e.map(pt))
		return rv
	def distance2(self,pt):
		# Returns the square of the shortest distance from pt to the object
		d2 = []
		for e in self.edges():
			d2.append(e.distance2(pt))
		return min(d2)
	def circumCircle(self,tol=0.0):
		return circle.Circle(self._boundingCircle.center.x,
				     self._boundingCircle.center.y,
				     self._boundingCircle.radius + tol)
	def bbox(self,tol=0.0):
		space = XY(float(tol),float(tol))
		return rectangle.Rectangle(self._bbox.ll-space,self._bbox.ur+space)
	def length(self):
		return reduce(lambda x,y:x+y,
			      map(lambda z: z.length(),self.edges())
			     )
	def _computeBbox(self):
		# Returns a Rect that bounds the object (used in __init__ and in methods
		# that modify the object).
		x = map(lambda(z):z.x,self.vertices)
		y = map(lambda(z):z.y,self.vertices)
		return rectangle.Rectangle(XY(min(x),min(y)),XY(max(x),max(y)))
	def _computeBoundingCircle(self):
		# Computes the smallest circle, centered about the center of the
		# bounding box, that contains all the vertices. This is NOT the
		# "minimum" bounding circle, but a "close enough" approximation
		# for use in searching methods.  Also called in __init__ and in methods
		# that modify the object.
		center = XY(0.5*(self._bbox.ll.x + self._bbox.ur.x), \
		            0.5*(self._bbox.ll.y + self._bbox.ur.y))
		d2 = []
		for p in self.vertices:
			d2.append(center.distance2(p))
		return circle.Circle(center.x,center.y,self.width+math.sqrt(max(d2)))
	def edges(self):
		rv = [ ]
		for i in range(len(self.vertices)-1):
			rv.append(line.Line(self.vertices[i],self.vertices[i+1],self.width))
		return rv
	#########################################################################################
	# Optional methods (default to no-action)
	#########################################################################################
	def draw(self,canvas=None):
		# Draws the object on the canvas (if provided)
		pass
	def erase(self,canvas=None):
		# Erases the object from the canvas (if provided)
		pass
	def select(self):
		# Selects the feature
		pass
	def deselect(self):
		# Deselects the feature
		pass
	def click(self,event):
		# Callback (optional) for mouse clicks
		pass
	def doubleClick(self,event):
		# Callback (optional) for mouse double-clicks
		pass
	def rightClick(self,event):
		# Callback (optional) for mouse right-clicks
		pass
	def mouseDrag(self,event):
		# Callback (optional) for mouse drags
		pass
	def mouseMove(self,event):
		# Callback (optional) for mouse moves
		pass
